﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using Grundy.Common;
using Grundy.Message;

namespace Grundy.CryptographicServices
{
    public abstract class CryptographicServicesProvider
    {
        private static RNGCryptoServiceProvider _nonceGenerator = new RNGCryptoServiceProvider();
        
        public CryptographicServicesProvider(string customerKey)
        {
        }

        private string GetNonce()
        {
            byte[] bytes = new byte[8];
            _nonceGenerator.GetBytes(bytes);
            return BitConverter.ToUInt64(bytes, 0).ToString();
        }
        
        public Container Sign(object obj, ContainerSigneeType signee = ContainerSigneeType.None)
        {
            var nonce = GetNonce();
            var s = GetNoncedPayload(obj, nonce);
            var bytes = s.ToByteArrayUsingUtf8();
            var signature = SignData(bytes);

            var container = new Container{Payload = s, Signature = signature.ToHexadecimalString(), Signee = signee, Provider = Provider, Algorithm = Algorithm};
            return  container;
        }

        private static string GetNoncedPayload(object o, string nonce)
        {
            var payload = o.Serialize();
            var indexOfStart = payload.LastIndexOf('}');
            return payload.Insert(indexOfStart, ", \"{0}\": \"{1}\"".FormatWith("Nonce", nonce)).Trim();
        }

        public T Verify<T>(Container container)
        {
            if (!CanVerifyProvider(container.Provider))
            {
                return default(T);
            }

            var signature = container.Signature.ToByteArray();
            var s = container.Payload;
            var bytes = s.ToByteArrayUsingUtf8();
            var result = VerifyData(bytes, signature);
            if (result)
            {
                return s.Deserialize<T>();
            }
            return default(T);
        }

        public abstract string Algorithm { get; }
        
        public abstract string Provider { get; }
      
        public virtual bool CanVerifyProvider(string providerString)
        {
            return Provider.EqualsIgnoreCase(providerString);
        }

        protected abstract byte[] SignData(byte[] data);
        protected abstract bool VerifyData(byte[] data, byte[] signature);

        public abstract byte[] EncryptData(byte[] data);
        public abstract byte[] DecryptData(byte[] data);
    }

    
    [DataContract]
    public class Fingerprint
    {
        
        
        
        
        
        
        
        
        
        [DataMember]
        public IdentifierCollection Identifiers { get; set; }

        [DataMember]
        public DateTime CreatedOn { get; set; }
    }
}